% Class for FunApp
% Properties:
%   1. Information about each dimension
%      UnitName (Cell(Dim x 1)); UnitLimit (Matrix(Dim x 2));
%      UnitNode (Cell(Dim x 1)); UnitNum (Vector(Dim)); 
%   2. Function Approximation
%      Space (Structure); Dim (Integer); Num (Integer)
%      Node (Matrix (Num x Dim)); IndNode (Matrix (Dim x n));
%      BasMat_0 (Sparse Matrix (Dim x Dim))
% Methods:
%   1. BasMat(Node)
classdef FunAppClass < handle
    properties
        UnitName
        UnitLimit
        UnitNode
        UnitNum
        
        Space
        Dim
        Num
        Node
        IndNode
        BasMat_0
        
        State
        BasMat_0_State
        MatSize_State
    end
    methods
        % Generate the Combined Function Approximation Structure
        function FunApp=FunAppClass(UnitName,FunAppStruct,GridLimit,PresetBreaks)
            if nargin<=3
                PresetBreaks    =   [];
            end
            FunApp.UnitName =   UnitName;
            FunApp.Dim      =   length(UnitName);
            FunAppCell      =   cell(FunApp.Dim,1);
            for ii=1:FunApp.Dim
                vv              =   UnitName{ii};
                if isempty(PresetBreaks)
                    FunAppCell{ii}  =   GenUnitFunApp(FunAppStruct.(vv),GridLimit.(vv),[]);
                else
                    FunAppCell{ii}  =   GenUnitFunApp(FunAppStruct.(vv),GridLimit.(vv),PresetBreaks.(vv));
                end
                    
            end
            
            % Space
            FunApp.Space    =   fundef(FunAppCell{:});
            % Node
            FunApp.UnitNode =   funnode(FunApp.Space);
            if FunApp.Dim==1
                FunApp.UnitNode     =   {FunApp.UnitNode};
            end
            FunApp.Node     =   gridmake(FunApp.UnitNode);
            
            % Dimension and Limits
            FunApp.Num      =   size(FunApp.Node,1);
            FunApp.UnitNum  =   zeros(FunApp.Dim,1);
            UnitIndNode     =   cell(FunApp.Dim,1);
            for dd=1:FunApp.Dim
                FunApp.UnitNum(dd)  =   length(FunApp.UnitNode{dd});
                UnitIndNode{dd}     =   (1:1:FunApp.UnitNum(dd))';
            end
            FunApp.IndNode  =   gridmake(UnitIndNode);
            FunApp.UnitLimit=   [FunApp.Space.a',FunApp.Space.b'];
            FunApp.BasMat_0 =   funbas(FunApp.Space,FunApp.Node,0);
        end
        function obj=AugmentState(obj,ExoState)
            N_ExoState  =   length(ExoState);
            ExoState    =   reshape(ExoState,[N_ExoState,1]);
            obj.State   =   gridmake(obj.Node,ExoState);
            obj.BasMat_0_State ...
                        =   SpaFun_Block2Diag(obj.BasMat_0,N_ExoState,'Matrix');
            obj.MatSize_State ...
                        =   [obj.Num,N_ExoState];
        end
    end
end

% Generate the Function Approximation Structure on each Dimension
% The format to specify the function approximation scheme is:
% Spline:  
%   {'spli',Breaks,0,Order} or 
%   {'spli',[Min;Max],Order,Number of Break Points};
% Chebychev: 
%   {'cheb',Number of nodes,Min,Max};
function UnitFunApp=GenUnitFunApp(AppStruct,Limit,PresetBreaks)
    Limit   =   reshape(Limit,[length(Limit),1]);
    switch AppStruct.AppType
        case 'spli'
            if AppStruct.BreakCurvature==0
                if isempty(PresetBreaks)
                    TempBreaks ...
                        =   VecFun_MakeLogDiffGrid(Limit(1),Limit(2),...
                                                   AppStruct.N,...
                                                   AppStruct.BreakCurvature)';
                else
                    TempBreaks ...
                        =   PresetBreaks(:);
                end
                UnitFunApp ...
                    =   {'spli',TempBreaks,0,AppStruct.PolyOrder};
%                         UnitFunApp ...
%                             =   {'spli',Limit,AppStruct.PolyOrder,AppStruct.N};
            else
                if isempty(PresetBreaks)
                    TempBreaks ...
                        =   VecFun_MakeLogDiffGrid(Limit(1),Limit(2),...
                                                   AppStruct.N,...
                                                   AppStruct.BreakCurvature)';
                else
                    TempBreaks ...
                        =   PresetBreaks(:);
                end
                
                UnitFunApp ...
                    =   {'spli',TempBreaks,0,AppStruct.PolyOrder};
            end
        case 'cheb'
            UnitFunApp ...
                =   {'cheb',AppStruct.N,Limit(1),Limit(2)};
        otherwise
            error('The Approximation Type is not Well-defined!');
    end
end